package com.damai.jobcenter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.core.jmx.JobDataMapSupport;
import org.quartz.impl.StdSchedulerFactory;
import org.w3c.dom.Element;

import com.damai.js.impl.JsJob;
import com.damai.plugins.BaseJob;
import com.damai.plugins.IPlugin;
import com.damai.utils.QuartzJob;
import com.damai.utils.QuartzJob.QuartzJobListener;
import com.damai.xml.XMLUtil;
import com.damai.xml.XMLVisitor;

public class QuartzPlugin implements IPlugin, XMLVisitor {
	private final Log logger = LogFactory.getLog(getClass());

	private static final QuartzPlugin ME = new QuartzPlugin();

	public static QuartzPlugin me() {
		return ME;
	}

	private QuartzPlugin() {

	}

	private SchedulerFactory schedulerFactory;
	private Scheduler scheduler;

	public void start() {

		schedulerFactory = new StdSchedulerFactory();

		try {
			scheduler = schedulerFactory.getScheduler();
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}

		if (scheduler == null) {
			throw new RuntimeException("scheduler is null");
		}

		try {
			loadProperties();
			logger.info("=======================Qrartz plugin startup success=======================");
		} catch (IOException e) {
			// throw new RuntimeException("Load job config error", e);
			logger.warn("Cannot find quartz config");
		}

	}
	
	
	private static class JobCreateInfo{
		public JobCreateInfo(Trigger trigger,JobDetail detail,String cron,String name){
			this.trigger = trigger;
			this.detail = detail;
			this.cron = cron;
			this.name = name;
		}
		private Trigger trigger;
		private JobDetail detail;
		private String name;

		private String cron;
		public JobDetail getDetail() {
			return detail;
		}
		
		public Trigger getTrigger() {
			return trigger;
		}

		public String getCron() {
			return cron;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		
		
		
	}

	private Map<String, JobCreateInfo> map = new ConcurrentHashMap<String, JobCreateInfo>();

	public void startJob(Class<? extends Job> jobClass, String cron) throws SchedulerException {
		startJob(jobClass, cron, jobClass.getSimpleName(), null);
	}

	public void startJob(String corn,String name,QuartzJobListener listener) throws SchedulerException{
		Map<String, Object> data = new HashMap<String, Object>();
		data.put(QuartzJob.LISTENER, listener);
		startJob(QuartzJob.class, corn, name, data);
	}
	
	/**
	 * 任务
	 * 
	 * @param jobName
	 * @param cron
	 * @param content
	 * @throws SchedulerException
	 */
	public void startJsJob(String jobName, String cron, String content) throws SchedulerException {
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("content", content);
		startJob(JsJob.class, cron, jobName, data);
	}

	public void startJob(Class<? extends Job> jobClass, String cron, String jobName, Map<String, Object> data)
			throws SchedulerException {
		if (!BaseJob.class.isAssignableFrom(jobClass)) {
			throw new RuntimeException("Job [" + jobClass.getName() + "] 必须继承com.citywithincity.plugins.BaseJob");
		}
		synchronized (map) {
			if(map.containsKey(jobName)){
				throw new SchedulerException(String.format("Job with name %s is already exists!", jobName));
			}
			JobBuilder jobBuilder = JobBuilder.newJob(jobClass).withIdentity("jobName", jobName);
			if (data != null) {
				jobBuilder.setJobData(JobDataMapSupport.newJobDataMap(data));
			}
			JobDetail job = jobBuilder.build();
			CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger", jobName)
					.withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
			scheduler.scheduleJob(job, trigger);
			scheduler.start();
			map.put(jobName, new JobCreateInfo(trigger,job ,cron,jobName));
		}

	}

	public void stopJob(String name) throws SchedulerException {
		synchronized (map) {
			if (!map.containsKey(name)) {
				return;
			}
			TriggerKey key = map.get(name).getTrigger().getKey();
			scheduler.unscheduleJob(key);
			map.remove(name);
		}
	}

	public Set<String> getJobNames() {
		synchronized (map) {
			return map.keySet();
		}
	}
	
	
	public String getType(String name){
		synchronized (map) {
			JobCreateInfo info = map.get(name);
			if(info==null){
				return null;
			}
			return info.getDetail().getJobClass().getName();
		}
	}
	
	public String getContent(String name){
		synchronized (map) {
			JobCreateInfo info = map.get(name);
			if(info==null){
				return null;
			}
			JobDataMap map = info.getDetail().getJobDataMap();
			if(map == null){
				return null;
			}
			return map.getString("content");
		}
	}
	

	@SuppressWarnings("unchecked")
	public void startJob(String className, String cron, String jobName, Map<String, Object> data)
			throws SchedulerException {
		Class<? extends Job> jobClass = null;
		try {
			jobClass = (Class<? extends Job>) Class.forName(className);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("创建job失败", e);
		}
		startJob(jobClass, cron, jobName == null ? jobClass.getSimpleName() : jobName, data);
	}

	public void startJob(String className, String cron) throws SchedulerException {

		startJob(className, cron, null, null);
	}

	public void load(Class<?> clazz, String name) throws IOException {
		XMLUtil.loadXmlLevel1(clazz, name, this);
	}

	public void stop() {
		if (map != null) {
			map.clear();
			map = null;
		}
		if (scheduler != null) {
			try {
				scheduler.shutdown();
			} catch (SchedulerException e) {
				logger.error(e);
			}
		}
	}

	private void loadProperties() throws IOException {
		XMLUtil.loadXmlLevel1("job.xml", this);
	}

	@Override
	public void onVisit(Element element) {
		String className = element.getAttribute("className");
		String cron = element.getAttribute("cron");
		String enable = element.getAttribute("enable");
		if (enable.equals("true")) {
			try {
				startJob(className, cron);
			} catch (SchedulerException e) {
				throw new RuntimeException("Fail to start job " + className, e);
			}

		}
	}

	public List<JobInfo> getJobs() {
		List<JobInfo> result = new ArrayList<JobInfo>();
		for (JobCreateInfo info : map.values()) {
			JobInfo i = new JobInfo();
			i.setName(info.getName());
			i.setCron(info.getCron());
			i.setClassName(info.getDetail().getJobClass().getName());
			
			i.setData(info.getDetail().getJobDataMap());
			result.add(i);
		}
		return result;
	}

}
